package goodash

import (
	"fmt"
	"math/rand/v2"
)

func Shuffle[T any](array []T, isNewSlice bool) []T {
	if len(array) == 0 {
		return array
	}
	newSlice := array
	if isNewSlice {
		newSlice = make([]T, len(array))
		copy(newSlice, array)
	}
	rand.Shuffle(len(newSlice), func(i, j int) {
		newSlice[i], newSlice[j] = newSlice[j], newSlice[i]
	})
	return newSlice
}

func Sample[T any](array []T) T {
	return array[rand.IntN(len(array))]
}

func SampleSize[T any](array []T, n int, isNewSlice bool) ([]T, error) {
	if n > len(array) {
		return nil, fmt.Errorf("n is greater than array length: n:%d, array.length:%d", n, len(array))
	}
	if n <= 0 {
		return nil, fmt.Errorf("n is less than or equal to 0")
	}
	newSlice := Shuffle(array, isNewSlice)
	return newSlice[:n], nil
}

// 倒序排序
func Reverse[T any](array []T, isNewSlice bool) {
	newSlice := array
	if isNewSlice {
		newSlice = make([]T, len(array))
		copy(newSlice, array)
	}
	length := len(newSlice)
	half := length / 2

	for i := 0; i < half; i = i + 1 {
		j := length - 1 - i
		newSlice[i], newSlice[j] = newSlice[j], newSlice[i]
	}
}

func Reduce[T any, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) R {
	for i := range collection {
		initial = accumulator(initial, collection[i], i)
	}

	return initial
}

func Uniq[T comparable, Slice ~[]T](collection Slice) Slice {
	result := make(Slice, 0, len(collection))
	seen := make(map[T]struct{}, len(collection))

	for i := range collection {
		if _, ok := seen[collection[i]]; ok {
			continue
		}
		seen[collection[i]] = struct{}{}
		result = append(result, collection[i])
	}
	return result
}

func UniqBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice {
	result := make(Slice, 0, len(collection))
	seen := make(map[U]struct{}, len(collection))

	for i := range collection {
		key := iteratee(collection[i])

		if _, ok := seen[key]; ok {
			continue
		}

		seen[key] = struct{}{}
		result = append(result, collection[i])
	}

	return result
}

func GroupBy[K comparable, T any](array []T, iteratee func(T) K) map[K][]T {
	result := make(map[K][]T, len(array))
	for _, item := range array {
		key := iteratee(item)
		result[key] = append(result[key], item)
	}
	return result
}

func GroupBy2[K comparable, T any, V any](array []T, iteratee func(T) (K, V)) map[K][]V {
	result := make(map[K][]V, len(array))
	for _, item := range array {
		key, v := iteratee(item)
		result[key] = append(result[key], v)
	}
	return result
}
func GroupBy2Map[K1 comparable, K2 comparable, T any](array []T, iteratee1 func(T) K1, iteratee2 func(T) K2) map[K1]map[K2]T {
	result := make(map[K1]map[K2]T, len(array))
	for _, item := range array {
		k1 := iteratee1(item)
		k2 := iteratee2(item)
		m := result[k1]
		if m == nil {
			m = make(map[K2]T)
			result[k1] = m
		}
		m[k2] = item
	}
	return result
}

func GroupBy2Map2[K1 comparable, K2 comparable, T any, V any](array []T, iteratee1 func(T) K1, iteratee2 func(T) (K2, V)) map[K1]map[K2]V {
	result := make(map[K1]map[K2]V, len(array))
	for _, item := range array {
		k1 := iteratee1(item)
		k2, v := iteratee2(item)
		m := result[k1]
		if m == nil {
			m = make(map[K2]V)
			result[k1] = m
		}
		m[k2] = v
	}
	return result
}
